from fastapi import APIRouter, HTTPException, APIRouter
from pydantic import BaseModel
from powder_packing.distribution_calculate import Dinger_Funk, compute_ideal_distribution
from powder_packing.sample_combinations import generate_combinations
import os
from utils import audit_action
from typing import List
from powder_packing.distribution_calculate import find_best_combination_optimized
import pandas as pd

DEFAULT_DA_DIR = "./storage"
os.makedirs(DEFAULT_DA_DIR, exist_ok=True)
densest_accumulation_router = APIRouter()


class DensestAccumulationModel(BaseModel):
    user_id: str
    file_names: List[str]
    n_comps: int = 0
    w: list = [0, 0, 0, 0]
    n: float = 0.23
    device: int = 0
    fill_rate: float = 0.98

@densest_accumulation_router.post("/densest_accumulation_all")
@audit_action("最紧密堆积理论计算")
async def densest_accumulation(request: DensestAccumulationModel):
    """
    1. Dinger Funk最紧密堆积理论

    2. Parameters:

        - user_id: str 用户id

        - file_names: List[str] 原始文件的文件名/路径名

        - n_comps: int 需要计算几元复配

        - w: list[float]  密度列表

        - n: float 分布模数 (default: 0.23)

        - device: [0, 1] 用来计算的设备 (default: 0, CPU)

        - fill_rate: float 0-1  填充率

    3. Return:

        - {
            
            "results":[
            
            {

                "Combination":[堆积计算采样的材料名],

                "R2":拟合优度的值,

                "V":[]  列表，里面是每个材料的体积分数，顺序和Combination里面对应

                "W":[]  列表 里面是每个材料的质量分数，顺序和Combination里面对应

                "Distribution": [] 列表， 实际曲线的Y值

            }
            
            ]   

            # results里面是所有组合的优化结果

            "x_values":[] 列表 X坐标值,

            "ideal_distribution": [] 列表  理想曲线的Y值

        }

    """
    # Call the Dinger_Funk function with the provided parameters
    # result = Dinger_Funk(request.file_name, request.n_comps, request.n, request.device)


    file_dir = os.path.join(DEFAULT_DA_DIR, request.user_id)
    os.makedirs(file_dir, exist_ok=True)
    device = "cpu" if request.device == 0 else "cuda"
    
    try:
        weight_map = {}
        if request.n_comps == None or request.n_comps == 0:
            request.n_comps = len(request.file_names)
        colunms = []
        data_dict = {}
        data_df = None
        # def process_data(file_path):
            
        
        for i, file_name in enumerate(request.file_names):
            weight_map[file_name] = request.w[i]
            file_path = os.path.join(file_dir, file_name)
            if not os.path.exists(file_path):
                raise HTTPException(status_code=404, detail="File not found.")
            # process_data(file_path)
            df = pd.read_csv(file_path, sep=r"\s+", encoding='utf-8', engine="python", header=None, index_col=None)
            if len(colunms) == 0:
                colunms = df.T.iloc[:1, :].to_numpy().tolist()[0]
            data_dict[file_name] = df.T.iloc[2:3, :].to_numpy().tolist()[0]
        if data_dict != {}:
            # data_df.columns
            # for k,v in data_dict.items():
            #     data = data.append(v)
            #     index = index.append(k)
            data_df:pd.DataFrame =  pd.DataFrame.from_dict(data_dict)# pd.concat(data, ignore_index=True)
            data_df = data_df.T
            data_df.columns = colunms
        

        comb_df = generate_combinations(request.file_names, sample_num=request.n_comps)
        D = list(map(float, data_df.columns))
        ideal_distribution = compute_ideal_distribution(D, n=request.n, device=device)
        results = find_best_combination_optimized(comb_df, row_data=data_df, ideal_distribution=ideal_distribution, num_samples=request.n_comps, max_bound=1, device=device)
        
        for i in range(len(results)):
            v_info = [request.fill_rate * v for v in results[i].get("V")]
            w_info = [weight_map[name] for name in results[i].get("Combination")]
            W = []
            all_W = sum([w*v for w,v in zip(w_info, v_info)])
            for w,v in zip(w_info, v_info):
                W.append(w*v / all_W if w*v != 0 else 0)
            results[i]["W"] = W
            results[i]["V"] = v_info
        return {"results":results, "x_values":D, "ideal_distribution":ideal_distribution.tolist()}


    except Exception as e:
        return HTTPException(500, detail=f"Error processing file: {str(e)}")



